package org.fdy.bgadmin.controller.security;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.captcha.*;
import cn.hutool.captcha.generator.CodeGenerator;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.fdy.bgadmin.dto.AuthUserDto;
import org.fdy.bgadmin.entity.SysRole;
import org.fdy.bgadmin.entity.SysUser;
import org.fdy.bgadmin.entity.SysUsersRoles;
import org.fdy.bgadmin.res.CaptchaRes;
import org.fdy.bgadmin.res.LoginRes;
import org.fdy.bgadmin.res.PublicKeyRes;
import org.fdy.bgadmin.service.SysRoleService;
import org.fdy.bgadmin.service.SysUserService;
import org.fdy.bgadmin.service.SysUsersRolesService;
import org.fdy.common.core.constant.RedisConstant;
import org.fdy.common.core.enums.CaptchaCategory;
import org.fdy.common.core.enums.CaptchaType;
import org.fdy.common.core.enums.ResultCode;
import org.fdy.common.core.exception.BaseException;
import org.fdy.common.core.model.Result;
import org.fdy.common.core.model.RsaDto;
import org.fdy.common.core.model.auth.SaUser;
import org.fdy.common.core.model.auth.UserInfo;
import org.fdy.common.core.units.ApiAssert;
import org.fdy.common.core.units.LoginUtils;
import org.fdy.common.core.units.RedisUtil;
import org.fdy.common.core.units.RsaUtils;
import org.fdy.common.framework.config.properties.CaptchaProperties;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author fangdy
 *
 * @create 2023-10-21 23:34
 */
@Slf4j
@RestController
@RequestMapping("/auth")
@RequiredArgsConstructor
@Api(tags = "系统：系统授权接口")
public class AuthorizationController {

    private final SysUserService sysUserService;
    private final SysRoleService sysRoleService;
    private final SysUsersRolesService sysUsersRolesService;

    private final CaptchaProperties captchaProperties;
    @ApiOperation("登录授权")
    @PostMapping(value = "/login")
    public Result<LoginRes> login(@Validated @RequestBody AuthUserDto authUser) throws Exception {
        // 验证码验证
        String captchaKey = String.format(RedisConstant.CAPTCHA_KEY, authUser.getUuid());
        String captchaCode = RedisUtil.getCacheObject(captchaKey);
        ApiAssert.isTrue(ResultCode.CAPTCHA_ERROR, StrUtil.isNotBlank(captchaCode));
        if (!captchaCode.equalsIgnoreCase(authUser.getCode())){
            throw new BaseException(ResultCode.CAPTCHA_ERROR);
        }
        RedisUtil.deleteObject(captchaKey);
        String rsaKey = String.format(RedisConstant.RSA_KEY, authUser.getKeyId());
        String privateKey = RedisUtil.getCacheObject(rsaKey);
        ApiAssert.isTrue(ResultCode.RSA_ERROR, StrUtil.isNotBlank(privateKey));
        // 密码解密
        String password = RsaUtils.decryptRsa(authUser.getPassword(),privateKey);
        RedisUtil.deleteObject(rsaKey);
        SysUser user = sysUserService.lambdaQuery().eq(SysUser::getUsername, authUser.getUsername())
                .last(" limit 1").one();
        ApiAssert.isTrue(ResultCode.PASSWORD_ERROR, Objects.nonNull(user));

        boolean matchesPassword = RsaUtils.matchesPassword(password, user.getPassword());
        ApiAssert.isTrue(ResultCode.PASSWORD_ERROR, matchesPassword);

        SaUser saUser = buidSaUser(user);
        LoginUtils.login(saUser);
        // 返回登录信息
        LoginRes data = new LoginRes();
        data.setToken(StpUtil.getTokenValue());
        data.setUserInfo(saUser.getUserInfo());
        return Result.ok(data);
    }

    private SaUser buidSaUser(SysUser user) {
        SaUser saUser = new SaUser();
        saUser.setUserId(user.getId());
        saUser.setUsername(user.getUsername());
        List<SysUsersRoles> sysUsersRoles = sysUsersRolesService.lambdaQuery()
                .eq(SysUsersRoles::getRoleId, user.getId()).list();
        Set<Long> roles = sysUsersRoles.stream().map(SysUsersRoles::getRoleId).collect(Collectors.toSet());
        List<SysRole> sysRoles = sysRoleService.lambdaQuery().in(SysRole::getId, roles).list();
        List<String> list = sysRoles.stream().map(SysRole::getName).collect(Collectors.toList());
        UserInfo userInfo = new UserInfo();
        userInfo.setId(user.getId());
        userInfo.setNickName(user.getNickName());
        userInfo.setRoles(list);
        saUser.setUserInfo(userInfo);
        return saUser;
    }

    @ApiOperation("获取用户信息")
    @GetMapping(value = "/info")
    public Result<SaUser> getUserInfo() {
        log.info("获取用户信息");
        SaUser loginUser = LoginUtils.getLoginUser();
        log.info("获取用户信息:{}",loginUser);
        return Result.ok(loginUser);
    }

    @ApiOperation("获取验证码")
    @GetMapping(value = "/code")
    public Result<CaptchaRes> getCode() {
        // 获取运算的结果
        // 保存验证码信息
        String uuid = IdUtil.simpleUUID();
        String verifyKey = String.format(RedisConstant.CAPTCHA_KEY, uuid);
        // 生成验证码
        String captchaType = captchaProperties.getType();
        boolean isMath = CaptchaType.CHARACTER.getCode().equals(captchaType);
        ApiAssert.isTrue(ResultCode.CAPTCHA_ERROR,isMath);
        CodeGenerator codeGenerator = ReflectUtil.newInstance(CaptchaType.CHARACTER.getClazz(),
                captchaProperties.getCharLength());
        String category = captchaProperties.getCategory();
        AbstractCaptcha captcha= null;
        if (CaptchaCategory.LINE.getCode().equalsIgnoreCase(category)){
            captcha = CaptchaUtil.createLineCaptcha(200, 100);
        }else if (CaptchaCategory.CIRCLE.getCode().equalsIgnoreCase(category)){
            captcha = CaptchaUtil.createCircleCaptcha(200, 100);
        }else if (CaptchaCategory.SHEAR.getCode().equalsIgnoreCase(category)){
            captcha = CaptchaUtil.createShearCaptcha(200, 100);
        }
        captcha.setGenerator(codeGenerator);
        captcha.createCode();
        String captchaCode = captcha.getCode();
        RedisUtil.setCacheObject(verifyKey,captchaCode,180,TimeUnit.SECONDS);
        return Result.ok(new CaptchaRes(uuid, captcha.getImageBase64()));
    }

    @ApiOperation("退出登录")
    @DeleteMapping(value = "/logout")
    public Result<Void> logout() {
        SaUser saUser = LoginUtils.getLoginUser();
        // todo 保存登出信息
        StpUtil.logout();
        return Result.ok();
    }

    @ApiOperation(value = "公钥获取")
    @PostMapping("/key")
    public Result<PublicKeyRes> getKey() {
        RsaDto dto = RsaUtils.generateRsa();
        String keyId = IdUtil.simpleUUID();
        PublicKeyRes res = new PublicKeyRes(keyId, dto.getPublicKey());
        String formatKey = String.format(RedisConstant.RSA_KEY, keyId);
        RedisUtil.setCacheObject(formatKey,dto.getPrivateKey(),180L,TimeUnit.SECONDS);
        return Result.ok(res);
    }
}
